/* ----------------------------------------------------------------------------------------------------------------------
 * Monitor
 * -----------------------------------------------------------------------------------------------------------------------
 */

/* 
   优化方案.<预> : 
	 
		1. 单Monitor情况下，使用单例Monitor.
		
		2. 判断世界上全部实体是否在Monitor范围内，从World移入，移除，或毁灭.
 */ 
package pure.engine 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import pure.engine.system.SystemManager;
	import pure.engine.world.WorldBody;
	import pure.engine.world.WorldContent;
	
	import pure.engine.entity.Entity;
	import pure.engine.entity.EntityManager;
	import pure.engine.entity.EntityPool;
	import pure.engine.entity.Mirror;
	import pure.engine.events.WorldContentEvent;
	import pure.engine.system.IFrameObject;
	import pure.engine.system.ProcessManager;
	import pure.engine.system.ns_despair;
	
	use namespace ns_despair;
	
	/**
	 * 监视器
	 * 
	 * @param ＃Pure
	 * 
	 * @see Despair - ＃createMonitor
	 */
public class Monitor extends Sprite implements IFrameObject
{
	public function Monitor(width:Number, height:Number, worldBody:WorldBody, autoNofityWorld:Boolean, transitionDuration:Number, transitionColor:uint)
	{
		_worldBody           =  worldBody;
		_autoNofityWorld     =  autoNofityWorld;
		_scope               =  new Rectangle(0, 0, width, height);
		_transitionDuration  =  _transitionCount  =  transitionDuration * 1000.0;
		_transitionColor     =  transitionColor;
		
		if (_worldBody.m_world)
		{
			_worldDirty = true;
		}
		else
		{
			_waiting = true;
		}
		this.mouseChildren = false;
		
		this.addEventListener(Event.ADDED_TO_STAGE, ____onAddedToStage);
	}
	
	
	
	// ----------------------------------［ Const ］----------------------------------

	public static const LEFT_TOP:String      =  'LT',
						RIGHT_TOP:String     =  'RT',
						LEFT_BOTTOM:String   =  'LB',
						RIGHT_BOTTOM:String  =  'RB';

	
	// ----------------------------------［ Model ］----------------------------------

	protected var _worldBody:WorldBody;
	
	protected var m_worldContent:WorldContent;
		
	protected var _scope:Rectangle;
	
	protected var _sceneryBmpList:Array;
	
	protected var _mirrorContainer:Sprite;
	
	protected var _mask:Shape;

	protected var _boundsWidth:int, _boundsHeight:int;
	
	protected var _align:String, _followed:String;
	
	protected var _waiting:Boolean// = true;
	
	// ----------------------------------［ Cache ］----------------------------------

	protected var _entityOldX:Number, _entityOldY:Number, _transitionDuration:Number;
	
	protected var _transitionColor:uint;

	protected var _transitionCount:int;

	protected var _worldDirty:Boolean, _alphaDirty:Boolean, _fading:Boolean, _scopeDirty:Boolean, _exiting:Boolean, _entering:Boolean;
	
	protected var _mouseRegistered:Boolean;    // 是否已注册滑鼠侦听

	protected var _autoNofityWorld:Boolean;    // 自动通知世界视域更新
	
	protected var _transitionBmd:BitmapData,_transitionBmd_B:BitmapData;
	
	protected var _transitionBmp:Bitmap, _transitionBmp_B:Bitmap;
	
	private static var cachedPoint:Point = new Point();
	
	private static var cachedMirrors:Array = [];
	
	private static var cachedColorTransform:ColorTransform = new ColorTransform();
	
	
	
	/**
	 * 毁灭
	 */
	public function destroy():void
	{
		ProcessManager.instance.removeFrameObject(this);
		SystemManager.m_notifier.removeEventListener(WorldContentEvent.WORLD_CHANGE,  ____onWorldChange);
		SystemManager.m_notifier.removeEventListener(WorldContentEvent.WORLD_WAITING, ____onWorldWaiting);
		
		if ( !_waiting ) 
		{
			_clearAllBuffers(true);
		}
		
		if ( _transitionBmd_B )
		{
			_transitionBmd_B.dispose();
			_transitionBmd_B  =  null;
			_transitionBmp_B  =  null;
		}
		
		_transitionBmd.dispose();
		_transitionBmd     =  null;
		_transitionBmp     =  null;
		this.mouseEnabled  =  false;
		_worldBody         =  null;
		m_worldContent         =  null;
		_sceneryBmpList    =  null;
		_mirrorContainer   =  null;
		_scope             =  null;
		_mask              =  null;
		this.mask          =  null;
	}

	
	/**
	 * 移动至世界坐标
	 * 
	 * @param wx
	 * @param wy
	 */
	public function moveInWorld(wx:Number, wy:Number):void
	{
		if (!m_worldContent||(_scope.x == wx && _scope.y == wy))  return;
		
		_scope.x     =  wx;
		_scope.y     =  wy;
		_scopeDirty  =  true;
		
		_clampInWorld();
	}
	
	
	/**
	 * 滚动指定坐标
	 * 
	 * @param x
	 * @param y
	 */
	public function scrollInWorld( x:Number, y:Number ):void 
	{
		if (x == 0 || y == 0)  return;
		
		_scope.x    +=  x;
		_scope.y    +=  y;
		_scopeDirty  =  true;
		
		_clampInWorld();
	}
	

	/**
	 * 追踪目标（不断通过实体管理器查找目标）
	 * 
	 * @param entityName
	 * @param boundsWidth
	 * @param boundsHeight
	 */
	public function follow( entity:String, boundsWidth:int = 0, boundsHeight:int = 0 ):void
	{
		if (boundsWidth  < 0)              throw new Error('Monitor - ＃follow - 参数错误，bounds宽度不可为负数.');
		if (boundsHeight < 0)              throw new Error('Monitor - ＃follow - 参数错误，bounds高度不可为负数.');
		if (boundsWidth  > _scope.width)   throw new Error('Monitor - ＃follow - 参数错误，bounds宽度超出监视器范围.');
		if (boundsHeight > _scope.height)  throw new Error('Monitor - ＃follow - 参数错误，bounds高度超出监视器范围.');
		
		_followed      =  entity;
		_boundsWidth   =  boundsWidth;
		_boundsHeight  =  boundsHeight;
	}

	
	public function onFrame(elapsedT:Number):void
	{
		var color:uint;
		var alpha:Number;
			
		// 淡出
		if ( _exiting )
		{		
			_transitionCount      +=  elapsedT;
			if (_transitionCount  >=  _transitionDuration) 
			{
				this.removeChild(_transitionBmp_B);
				_transitionBmd_B.dispose();
				_transitionBmd_B   =  null;
				_transitionBmp_B   =  null;
				_transitionCount   =  _transitionDuration;
				_exiting           =  false;
				_waiting           =  true;
				alpha              =  0xFF;
			}
			else
			{
				alpha              =  int((_transitionCount / _transitionDuration) * 256)
			}
			color  =  _transitionColor | (alpha << 24);
			_transitionBmd.fillRect(_transitionBmd.rect, color);
			return;
		}

		// 新世界
		if ( _worldDirty )
		{
			_worldDirty = false;
			if (_worldBody.m_world)
			{
				// 更新世界
				_advanceWorld();
				
				// 更新残像(FORCE)
				_advanceMirror(true);
				
				// 加入滑鼠侦听
				if (this.mouseEnabled)
				{
					_registerMouseListeners();
				}
				
				_waiting   =  false;
				_entering  =  true;
			}
			else
			{
				return;
			}
		}

		// 等待
		if ( _waiting )  return;

		// 淡入
		if ( _entering )
		{
			_transitionCount      -=  elapsedT;
			if ( _transitionCount <=  0 ) 
			{
				this.removeChild( _transitionBmp );
				_transitionCount   =  0;
				_entering          =  false; 
			}
			else
			{
				alpha  =  int((_transitionCount / _transitionDuration) * 256)
				color  =  _transitionColor | (alpha << 24);
				_transitionBmd.fillRect( _transitionBmd.rect, color );
				this.setChildIndex( _transitionBmp, this.numChildren - 1 );
			}
		}
		
		// 视野
		if (_scopeDirty)
		{
			m_worldContent.updateSceneriesByArea( _scope, _sceneryBmpList );
			if(_autoNofityWorld)
			{
				_nofityScopeUpdate();
			}
			_scopeDirty = false;
		}
		
		// 更新追踪(视域)
		if (_followed != null && _followed != '')
		{
			_advanceFollow();
		}

		// 更新残像
		_advanceMirror(false);

	}
	

	
	/* ----------------------------------------------------------------------------------------------------------------------
	 * ★★★★★★★★★★★★★★★★  get × set  ★★★★★★★★★★★★★★★★
	 * ----------------------------------------------------------------------------------------------------------------------
	 */
	

	 
	/**
	 * 是否侦听滑鼠
	 */
	override public function set mouseEnabled(flag:Boolean):void
	{
		super.mouseEnabled = flag;
		if (flag)
		{
			if (!_mouseRegistered && !_waiting)
			{
				_registerMouseListeners();
			}
		}
		else
		{
			if (_mouseRegistered)
			{
				_unregisterMouseListeners();
			}
		}
	}
	
	
	/**
	 * 对齐
	 */
	public function set align(value:String):void
	{
		_align = value;
		if (!_waiting)
		{
			_doAlign();
		}
	}
	
	
	/**
	 * 视野
	 */
	public function get scope():Rectangle { return _scope; }
	
	
	/**
	 * 滑鼠世界位置
	 */
	public function get mouseWorldX():Number { return this.mouseX + _scope.x; }
	public function get mouseWorldY():Number { return this.mouseY + _scope.y; }
	
	
	
	
	/* ----------------------------------------------------------------------------------------------------------------------
	 * ★★★★★★★★★★★★★★★★  private  ★★★★★★★★★★★★★★★★
	 * ----------------------------------------------------------------------------------------------------------------------
	 */
	

	 
	protected function ____onAddedToStage(e:Event):void
	{
		this.removeEventListener( Event.ADDED_TO_STAGE, ____onAddedToStage );

		SystemManager.m_notifier.addEventListener(WorldContentEvent.WORLD_CHANGE,  ____onWorldChange);
		SystemManager.m_notifier.addEventListener(WorldContentEvent.WORLD_WAITING, ____onWorldWaiting);
		ProcessManager.instance.addFrameObject(this, -10);

		_sceneryBmpList   =  [];
		_mirrorContainer  =  new Sprite();

		// 遮罩
		_mask             =  new Shape();
		this.mask         =  _mask;
		this.addChild(_mask);
		_mask.graphics.beginFill(0, 0);
		_mask.graphics.drawRect(0, 0, _scope.width, _scope.height);

		// 过渡
		_transitionBmd    =  new BitmapData(_scope.width, _scope.height, true, 0x0);
		_transitionBmp    =  new Bitmap(_transitionBmd);
		_transitionBmd.fillRect(_transitionBmd.rect, _transitionColor | (0xFF << 24));
		this.addChild(_transitionBmp);
	}
	 
	
	 
	/**
	 * 对齐
	 */
	protected function _doAlign():void 
	{
		switch( _align ) 
		{
			case LEFT_TOP:
				_scope.x  =  0;
				_scope.y  =  0;
				break;
				
			case LEFT_BOTTOM:
				_scope.x  =  0;
				_scope.y  =  m_worldContent.m_height -  _scope.height;
				break;
				
			case RIGHT_TOP:
				_scope.x  =  m_worldContent.m_width  -  _scope.width;
				_scope.y  =  0;
				break;
				
			case RIGHT_BOTTOM:
				_scope.x  =  m_worldContent.m_width  -  _scope.width;
				_scope.y  =  m_worldContent.m_height -  _scope.height;
				break;
				
			default:
				break;
		}
	}
	
	
	/**
	 * 执行更新世界
	 */
	protected function _advanceWorld():void
	{
		m_worldContent = _worldBody.m_world;
		
		if ( m_worldContent.m_width < _scope.width || m_worldContent.m_height < _scope.height )
		{
			throw new Error("Monitor - ＃ ____onWorldChange - 监视范围超出世界.");
		}
		
		_makeAllBuffers();
		_doAlign();
	}
	
	
	/**
	 * 生成全部缓存
	 */
	protected function _makeAllBuffers():void 
	{
		var buffer:BitmapData;
		var bmp:Bitmap;
		var list:Array;
		var len:int;
		
		list  =  m_worldContent.sceneryBuffers;
		len   =  list.length;
		
		for (var i:int = 0; i < len; i++)
		{
			buffer  =  list[i]
			bmp     =  new Bitmap(buffer.clone());
			this.addChild(bmp);
			_sceneryBmpList.push(bmp);
		}
		this.addChild(_mirrorContainer);
	}
	

	/**
	 * 清除全部缓存
	 * 
	 * @param entire
	 */
	protected function _clearAllBuffers(entire:Boolean):void
	{
		var bmp:Bitmap;
		var M:Mirror;
		var len:int, i:int;
		
		while (this.numChildren)  removeChildAt(0);
		
		while (_mirrorContainer.numChildren)
		{
			M = _mirrorContainer.removeChildAt(0) as Mirror;
			M.recycle();
		}

		if (!entire)
		{
			this.addChild(_mask);
		}
		
		len = _sceneryBmpList.length;
		for (i = 0; i < len; i++)
		{
			bmp = _sceneryBmpList[i];
			bmp.bitmapData.dispose();
			bmp.bitmapData = null;
		}
		_sceneryBmpList.length = 0;
	}

	
	/**
	 * 执行更新镜头追踪（实体）
	 */
	private function _advanceFollow():void
	{
		var list:Array;
		var E:Entity
		var flagH:Boolean, flagV:Boolean;

		list = EntityManager.m_entityGroups[_followed];
		if (list)
		{
			E      =  list[0];
			flagH  =  _rejustHorizontal(E);
			flagV  =  _rejustVertical(E);
			
			if (flagH || flagV)
			{
				_clampInWorld();
				_scopeDirty = true;
			}
		}
	}
	
	
	/**
	 * 执行更新残像
	 * 
	 * @param force
	 */
	private function _advanceMirror(force:Boolean):void
	{
		var lenC:int, i:int, lenA:int;
		var M:Mirror;
		var E:Entity;
		var list:Array;
		
		list  =  _worldBody.m_activeList;
		lenA  =  _worldBody.m_numActive;
		lenC  =  _mirrorContainer.numChildren;
		
		if (!force)
		{
			for (i = lenC - 1; i >= 0; i--)
			{
				M = _mirrorContainer.getChildAt(i) as Mirror;
				if (!M.entity.m_visible)
				{
					_mirrorContainer.removeChild(M);
					M.entity.mirrorExisted = false;
					M.recycle();
				}
				else
				{
					E    =  M.entity;
					M.x  =  E.m_x - _scope.x - E.m_range;
					M.y  =  E.m_y - _scope.y - E.m_range;
					
					M.update();
					cachedMirrors.push(M);
					M.entity.mirrorExisted = true;
				}
			}

			for (i = 0; i < lenA; i++)
			{
				E = list[i];
				if (!E.mirrorExisted)
				{
					M    =  EntityPool.makeMirror();
					M.x  =  E.m_x - _scope.x - E.m_range;
					M.y  =  E.m_y - _scope.y - E.m_range;
					
					M.binding(E);	
					cachedMirrors.push(M);
					_mirrorContainer.addChild(M);
				}
			}
		}
		
		else
		{
			for (i = lenC - 1; i >= 0; i--)
			{
				M = _mirrorContainer.removeChildAt(i) as Mirror;
				M.entity.mirrorExisted = false;
				M.recycle();
			}
			
			for (i = 0; i < lenA; i++)
			{
				E    =  list[i];
				M    =  EntityPool.makeMirror()
				M.x  =  E.m_x - _scope.x - E.m_range;
				M.y  =  E.m_y - _scope.y - E.m_range;
				
				M.binding(E);	
				cachedMirrors.push(M);
				_mirrorContainer.addChild(M);
			}
		}
		
		cachedMirrors.sortOn(_worldBody.m_sortData, Array.NUMERIC);
		lenC = cachedMirrors.length;
		for (i = 0; i < lenC; i++)
		{
			M = cachedMirrors[i];
			if(_mirrorContainer.getChildIndex(M)!=i)
			{
				_mirrorContainer.setChildIndex(M, i);
			}
		}
		cachedMirrors.length = 0;
	}
	

	/**
	 * 保持视野在世界范围
	 */
	private function _clampInWorld():void 
	{
		if (_scope.x < 0)                         _scope.x  =  0;
		if (_scope.y < 0)                         _scope.y  =  0;
		if (_scope.right  > m_worldContent.m_width)   _scope.x  =  m_worldContent.m_width  - _scope.width;
		if (_scope.bottom > m_worldContent.m_height)  _scope.y  =  m_worldContent.m_height - _scope.height;
	}
	
	
	/**
	 * 水平调整位置
	 */
	private function _rejustHorizontal(E:Entity):Boolean
	{
		var trackedX:Number;
		
		if (E.m_x == _entityOldX)  return false;
		
		if (_boundsWidth == 0) 
		{
			_scope.x = E.m_x - (_scope.width >> 1);
		}
		else
		{
			trackedX = E.m_x - _scope.x;
			if (trackedX < (_scope.width - _boundsWidth) >> 1 ) 
			{
				trackedX  =  (_scope.width - _boundsWidth) >> 1;
				_scope.x  =  E.m_x - trackedX;
			}
			else if (trackedX > (_scope.width + _boundsWidth) >> 1 )
			{
				trackedX  =  (_scope.width + _boundsWidth) >> 1;
				_scope.x  =  E.m_x - trackedX;
			}
		}
		_entityOldX = E.m_x;
		return true;
	}
	
	
	/**
	 * 垂直调整位置
	 */
	private function _rejustVertical(E:Entity):Boolean 
	{
		var trackedY:Number;
		
		if (E.m_y == _entityOldY)  return false;
		
		if (_boundsHeight == 0) 
		{
			_scope.y = E.m_y - (_scope.height >> 1);
		}
		else
		{
			trackedY = E.m_y - _scope.y;
			if (trackedY < (_scope.height - _boundsHeight) >> 1 ) 
			{
				trackedY = (_scope.height - _boundsHeight) >> 1;
				_scope.y = E.m_y - trackedY;
			}
			else if (trackedY > (_scope.height + _boundsHeight) >> 1 )
			{
				trackedY = (_scope.height + _boundsHeight) >> 1;
				_scope.y = E.m_y - trackedY;
			}
		}
		_entityOldY = E.m_y;
		return true
	}

	
	/**
	 * 等待中
	 */
	protected function ____onWorldWaiting(e:WorldContentEvent):void 
	{
		if (_exiting || _worldBody.m_name != e.worldBodyName)  return;
		
		if (_entering)
		{
			_entering    =  false;
			_worldDirty  =  true;
		}
		
		if (!_transitionBmd_B)
		{
			_transitionBmd_B  =  new BitmapData( _scope.width, _scope.height, true, 0x0 );
			_transitionBmp_B  =  new Bitmap( _transitionBmd_B );
			_transitionBmd_B.draw(this);
		}
		
		if (_mouseRegistered)
		{
			_unregisterMouseListeners();
		}
		
		_clearAllBuffers(false);
		this.addChild(_transitionBmp_B);
		this.addChild(_transitionBmp);

		_transitionCount  =  0;
		_exiting          =  true;
		_waiting          =  true;
		m_worldContent        =  null;
	}
	

	/**
	 * 新世界
	 */
	protected function ____onWorldChange(e:WorldContentEvent):void
	{	
		if (_worldBody.m_name != e.worldBodyName)  return;

		_worldDirty  =  true;
		_scopeDirty  =  true;
	}

	
	/**
	 * 注册滑鼠侦听
	 * 
	 * <TODO> 进入新世界时，重新注册
	 */
	protected function _registerMouseListeners():void
	{
		this.addEventListener( MouseEvent.MOUSE_DOWN, ____onMouseInteract );
		this.addEventListener( MouseEvent.MOUSE_UP,   ____onMouseInteract );
		this.addEventListener( MouseEvent.MOUSE_MOVE, ____onMouseInteract );
		_mouseRegistered = true;
	}
	
	
	/**
	 * 注销滑鼠侦听
	 * 
	 * <TODO> 离开世界时调用
	 */
	protected function _unregisterMouseListeners():void
	{
		this.removeEventListener(MouseEvent.MOUSE_DOWN, ____onMouseInteract);
		this.removeEventListener(MouseEvent.MOUSE_UP,   ____onMouseInteract);
		this.removeEventListener(MouseEvent.MOUSE_MOVE, ____onMouseInteract);
		_mouseRegistered = false;
	}
	
	
	/**
	 * 滑鼠交互侦听
	 */
	protected function ____onMouseInteract(e:MouseEvent):void
	{
		e.updateAfterEvent();
		
		var mirrors:Array
		var M:Mirror;
		var len:int;
		
		cachedPoint.x  =  e.stageX;
		cachedPoint.y  =  e.stageY;
		mirrors        =  this.getObjectsUnderPoint(cachedPoint);
		len            =  mirrors.length;
		
		while(--len > -1)
		{
			if (!(mirrors[len] is Mirror))  continue;
			
			M  =  mirrors[len];
			if (M.entity.interactive)
			{
				if (M.bitmapData.getPixel32((M.mouseX + 0.5 >> 0), (M.mouseY + 0.5 >> 0)) > 0)
				{
					_worldBody.updateMouseState( e.type, M, this.mouseX + _scope.x, this.mouseY + _scope.y, cachedPoint.x, cachedPoint.y);
					return;
				}
			}
		}
		_worldBody.updateMouseState(e.type, null, this.mouseX + _scope.x, this.mouseY + _scope.y, cachedPoint.x, cachedPoint.y);
	}
	
	
	/**
	 * 通知更新视域
	 */
	protected function _nofityScopeUpdate():void
	{
		m_worldContent.m_top     =  _scope.y >> 0;
		m_worldContent.m_bottom  =  _scope.bottom >> 0;
		m_worldContent.m_left    =  _scope.left >> 0;
		m_worldContent.m_right   =  _scope.right >> 0;
		
		//trace('\n[Scope]      ' + (m_worldContent.m_top >> 0)                                                               + '\n'
		                        //+ '        '     + (m_worldContent.m_left >> 0) + '       ' + (m_worldContent.m_right >> 0) + '\n'
							    //+ '            ' + (m_worldContent.m_bottom >> 0));
	}

	
}

}